/**
 * OSAmI: Open Source Ambient Intelligence
 * Copyright (C) 2010 Thales Communications, SA
 * Contact: osami-general@osami-commons.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */
package org.osami.commons.encryption.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.osami.commons.encryption.iface.SymmetricEncryptionService;
import org.osami.commons.encryption.iface.exception.EncryptionException;

/**
 * Provides an implementation for the encryption service
 * @author Marcel Arrufat Arias
 *
 */
public class SymmetricEncryptionServiceImpl implements SymmetricEncryptionService {

	private SecretKeySpec key;
	private Cipher cipher;

	/**
	 * Decrypts the given array with the previously loaded key
	 */
	public byte[] decrypt(byte[] encryptedByteArray) throws EncryptionException {

		byte[] clearByteArray = null;

		clearByteArray = processData(Cipher.DECRYPT_MODE,encryptedByteArray);

		return clearByteArray;
	}


	/**
	 * Encrypts the given byte array with the previously loaded key
	 */
	public byte[] encrypt(byte[] clearByteArray) throws EncryptionException {

		byte[] encryptedByteArray = null;

		encryptedByteArray = processData(Cipher.ENCRYPT_MODE,clearByteArray);

		return encryptedByteArray;
	}


	/**
	 * Loads a symmetric key, that uses the given algorithm, which is placed in the
	 * selected file
	 */
	public void loadSymmetricKeyFromFile(File file,String algorithm) throws EncryptionException {

		try {

			byte[] bytearray = loadByteArrayFromFile(file);
			this.key = new SecretKeySpec(bytearray, algorithm);
			cipher = Cipher.getInstance(algorithm);

		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			throw new EncryptionException("Invalid algorithm");
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new EncryptionException("Invalid padding");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new EncryptionException("Could not find file");
		}

	}

	/**
	 * Helper method to encrypt/decrypt data, depending on the mode
	 * @param mode
	 * @param byteArraySrc
	 * @return
	 */
	private byte[] processData(int mode, byte[] byteArraySrc) {

		byte[] byteArrayDst = null;
		try {
			cipher.init(mode, key);
			byteArrayDst = cipher.doFinal(byteArraySrc);

		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return byteArrayDst;

	}

	/**
	 * Loads the content of the file as a byte array
	 * @param file
	 * @return
	 * @throws IOException
	 */
	private byte[] loadByteArrayFromFile(File file) throws IOException {
		// TODO Auto-generated method stub
		FileInputStream fin = new FileInputStream(file);

		/*
		 * Create byte array large enough to hold the content of the file.
		 * Use File.length to determine size of the file in bytes.
		 */


		byte fileContent[] = new byte[(int)file.length()];

		/*
		 * To read content of the file in byte array, use
		 * int read(byte[] byteArray) method of java FileInputStream class.
		 *
		 */

		fin.read(fileContent);


		return fileContent;
	}

}
